﻿using System;
using System.Data.Common;
using System.Data.SqlClient;
using JetBrains.Annotations;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using MySql.Data.MySqlClient;
using Npgsql;
using NpgsqlTypes;
using static Abp.SequentialGuidGenerator;

namespace ARchGL.Platform.EntityFrameworkCore
{
    public static class DatabaseTypeHelper
    {
        public static SequentialGuidDatabaseType DatabaseType { get; set; }

        /// <summary>
        /// 获取当前数据库类型
        /// </summary>
        /// <param name="connection"></param>
        /// <returns></returns>
        public static SequentialGuidDatabaseType GetDatabaseType(string connection)
        {
            if (DatabaseType > 0) return DatabaseType;

            var conn = connection.ToLowerInvariant();
            if (conn.Contains("host=") || conn.Contains("postgres"))
                return DatabaseType = SequentialGuidDatabaseType.PostgreSql;
            else if (conn.Contains("server=") || conn.Contains("root"))
                return DatabaseType = SequentialGuidDatabaseType.MySql;
            else if (conn.Contains("data source=") && conn.Contains("initial catalog="))
                return DatabaseType = SequentialGuidDatabaseType.SqlServer;

            return SequentialGuidDatabaseType.Oracle;
        }

        /// <summary>
        /// 执行原生 SQL 语句
        /// </summary>
        /// <param name="db"></param>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        public static void Execute(this DbContext db, string sql, [NotNull] params object[] parameters)
        {
            var par = sql.Split('@');
            var obj = new object[par.Length - 1];

            for (int i = 1; i < par.Length; i++)
            {//组合 parameter 数组，主要是给列名加上 @ 符号
                var val = par[i];

                if (val.IndexOf(',') != -1)
                    val = val.Split(',')[0];

                var length = val.IndexOf(' ');
                if (length == -1) length = val.Length; //兼容最后一个参数

                var parameterName = "@" + val.Trim().Substring(0, length);
                obj[i - 1] = GetParameter(parameterName, parameters[i - 1]);
            }

            if (DatabaseType == SequentialGuidDatabaseType.PostgreSql)
            {//当数据库是 PostgreSQL 时给每个字段加上双引号，因为 PostgreSQL 对大小写敏感
                var asdf = sql.Split('=');
                var symbol = ' ';
                foreach (var item in asdf)
                {
                    var str = item;
                    if (item.Contains(" IS NULL"))
                    {//排除 NULL 语法
                        str = item.Replace("IS NULL", "");
                    }
                    symbol = ' ';
                    var index = str.LastIndexOf(symbol);
                    if (index == -1)
                    {
                        symbol = ',';
                        index = str.LastIndexOf(symbol);
                        if (index == -1) continue;
                    }
                    var subsection = str.Trim().Split(symbol);
                    var source = subsection[subsection.Length - 1].Replace("!", "");
                    sql = sql.Replace(str, str.Replace(source, "\"" + source.Trim() + "\""));
                }
            }

            db.Database.ExecuteSqlCommand(sql, obj);
        }

        public static void WriteAndSetDbType<T>(this NpgsqlBinaryImporter n, T value)
        {
            //n.Write(value, value.GetDbType());
        }

        public static NpgsqlDbType GetDbType(this Type type)
        {
            if (DatabaseType == SequentialGuidDatabaseType.PostgreSql)
            {
                //var type = obj.GetType();
                if (type == typeof(DateTime)) return NpgsqlDbType.Timestamp;
                if (type == typeof(Guid)) return NpgsqlDbType.Uuid;
                if (type == typeof(string)) return NpgsqlDbType.Varchar;
                if (type == typeof(decimal)) return NpgsqlDbType.Numeric;

            }
            return NpgsqlDbType.Varchar;
        }

        private static object GetParameter(string name, object par)
        {
            switch (DatabaseType)
            {
                case SequentialGuidDatabaseType.PostgreSql: return new NpgsqlParameter(name, par);
                case SequentialGuidDatabaseType.MySql: return new MySqlParameter(name, par);
                case SequentialGuidDatabaseType.SqlServer: return new SqlParameter(name, par);
                //case SequentialGuidDatabaseType.Oracle: return new OracleParameter(name, par);
                //case SequentialGuidDatabaseType.Sqlite: return new SqliteParameter(name, par);

                default: return new MySqlParameter(name, par);
            }
        }
    }
}
